package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"syscall"

	"gitee.com/k3x/fer"
)

func main() {
	var boc, cmb, icbc bool
	flag.BoolVar(&boc, "boc", boc, "中国银行外汇牌价")
	flag.BoolVar(&cmb, "cmb", cmb, "招商银行外汇牌价")
	flag.BoolVar(&icbc, "icbc", icbc, "工商银行外汇牌价")
	flag.Usage = func() {
		n := filepath.Base(os.Args[0])
		fmt.Fprintf(os.Stderr, "命令: %s -cmb: 列出招商银行外汇牌价\n", n)
		fmt.Fprintf(os.Stderr, "命令: %s -boc: 列出中国银行外汇牌价\n", n)
		fmt.Fprintf(os.Stderr, "命令: %s -icbc: 列出工商银行外汇牌价\n", n)
		fmt.Fprintf(os.Stderr, "使用 `%s 金额` 计算兑换人民币元金额, 如\n", n)
		fmt.Fprintf(os.Stderr, "  %s USD 199\n", n)
		fmt.Fprintf(os.Stderr, "  %s \\$199\n", n)
		fmt.Fprintf(os.Stderr, "  %s 199EUR\n", n)
		fmt.Fprintf(os.Stderr, "  %s €199.88\n", n)
		fmt.Fprintf(os.Stderr, "  %s HKD199.88\n", n)
	}
	flag.Parse()

	if len(os.Args) == 1 {
		flag.Usage()
		return
	}

	ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL)
	defer cancel()

	if boc || cmb || icbc {
		if cmb {
			console(ctx, fer.FromCMB)
		}
		if boc {
			console(ctx, fer.FromBOC)
		}
		if icbc {
			console(ctx, fer.FromICBC)
		}
		return
	}

	calc(ctx, strings.Join(flag.Args(), " "))
}

func console(ctx context.Context, fetches ...fer.Fetch) {
	for _, fetch := range fetches {
		rates, err := fetch(ctx)
		if err != nil {
			log.Fatalf("%+v", err)
		}
		rates.WriteTo(os.Stdout)
	}
}

func calc(ctx context.Context, arg string) {
	arg = strings.ToUpper(arg)
	if num, _ := strconv.ParseFloat(strings.Trim(regexp.MustCompile(`[\d]+(\.\d+)?`).FindString(arg), "."), 0); num > 0 {
		code := regexp.MustCompile(fmt.Sprintf(`(%s)`, strings.ReplaceAll(strings.Join(fer.Codes(), "|"), "$", "\\$"))).FindString(arg)
		if code = fer.GetCode(code); code == "" {
			return
		}
		label := fer.GetLabel(code)
		if rates, err := getFetch(code)(ctx); err == nil {
			if r := getRate(rates.Data[code]); r > 0 {
				fmt.Printf("%.2f%s = %.2f人民币元 (%s人民币元汇率: %.2f) -- %s\n", num, label, r*num/100, label, r, rates.From)
				return
			}
		}
	}
}

func getFetch(code string) fer.Fetch {
	switch code {
	case "CAD", "GBP", "JPY", "SGD", "CHF", "HKD", "NZD", "AUD", "USD", "EUR":
		return fer.FromCMB
	case "DKK", "KRW", "KZT", "MOP", "MYR", "NOK", "PKR", "RUB", "SEK", "THB", "ZAR":
		return fer.FromICBC
	case "AED", "BRL", "IDR", "INR", "PHP", "SAR", "TRY", "TWD":
		return fer.FromBOC
	default:
		return func(ctx context.Context) (result fer.Rates, err error) {
			return
		}
	}
}

func getRate(rate fer.Rate) float64 {
	r := rate.CashSellingRate
	if r == 0 {
		r = rate.SellingRate
	}
	if r == 0 {
		r = rate.CashBuyingRate
	}
	if r == 0 {
		r = rate.BuyingRate
	}
	if r == 0 {
		r = rate.MiddleRate
	}
	return r
}
